%     FASTENER DOCTOR - программа для лечения и конвертации крепежа
%     Copyright (C) 2018  Михаил Козлов
%     Сайт проекта: <http://www.fastenerdoctor.ru/>
%     Адрес электронной почты для связи с разработчиком:
%     <doctor.fastener@gmail.com>
% 
%     Эта программа является свободным программным обеспечением: Вы можете
%     распространять ее и/или изменять, соблюдая условия Универсальной общественной
%     лицензии GNU, опубликованной Фондом свободного программного обеспечения,
%     либо редакции 3, либо (на Ваше усмотрение) любой редакции, выпущенной позже.
% 
%     Эта программа распространяется в надежде на то, что она окажется полезной,
%     но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, включая даже подразумеваемую гарантию ТОВАРНОЙ ПРИГОДНОСТИ,
%     либо ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Ознакомьтесь с Универсальной общественной
%     лицензией GNU для получения более подробной информации.
% 
%     Вы должны были получить копию Универсальной общественной лицензии GNU
%     вместе с этой программой. Если Вы ее не получили, то перейдите по адресу:
%     <http://www.gnu.org/licenses/>.
% 
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
% 
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%     GNU General Public License for more details.
% 
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <https://www.gnu.org/licenses/>.

function [An_Coord_Repair_Counter, Ref_Coord_Repair_Counter] = Repair_RutFast_Coord(Fastener_Index)

global ErrCode;

global DB_NODES;
global DB_MPC;
global DB_ELEMENTS;
global DB_PSETS;
global DB_MATERIALS;

global AT_NODES;
global AT_MPC;
global AT_ELEMENTS;
global AT_PSETS;
global AT_MATERIALS;

global DB_FASTENERS;

global db_Next_Coord_Number;

% Инициализация счетчиков
An_Coord_Repair_Counter = 0;
Ref_Coord_Repair_Counter = 0;

% Получение оси болта
[Axis_Point_1, Axis_Point_2, Axis_vector] = Get_Fastener_Axis(Fastener_Index);

if (ErrCode < 0)
    return;
end

% Получение списка систем координат, в которых задана ориентация CBUSH элементов, моделирующих смятие в композите
CM_Coord_list = zeros(100,1);

counter = 0;

for i=1:DB_FASTENERS{Fastener_Index}.N_layers

    % Ветвление по типу присоединенной топологии
    switch DB_FASTENERS{Fastener_Index}.Joint_topology{i}

        case {'plate', 'rbe2_2d_hole', 'bar_spider_2d_hole', 'plate_rbe2_sequential', 'plate_rbe2_parallel'}

            pset_type = DB_PSETS{AT_PSETS(DB_ELEMENTS{AT_ELEMENTS(DB_FASTENERS{Fastener_Index}.Joint_mesh{i}.Element_list(1))}.Prop_ID)}.type;

            switch pset_type

                case {'PCOMP', 'PCOMPG'}

                    CM_Flag = 1;

                case 'PSHELL'

                    % Проверка типа материала
                    mat_type = DB_MATERIALS{AT_MATERIALS(DB_PSETS{AT_PSETS(DB_ELEMENTS{AT_ELEMENTS(DB_FASTENERS{Fastener_Index}.Joint_mesh{i}.Element_list(1))}.Prop_ID)}.Mat_ID_membrane)}.type;

                    switch mat_type
                        case 'isotropic'
                            CM_Flag = 0;
                        case '2d_orthotropic'
                            CM_Flag = 1;
                        case '2d_anisotropic'
                            CM_Flag = 1;
                        otherwise
                            fprintf (1, '\nОшибка функции Repair_RutFast_Coord: недопустимый тип материала [%s] среди элементов присоединенной топологии [%s] болта %i !\n', mat_type, DB_FASTENERS{Fastener_Index}.Joint_topology{i}, Fastener_Index);
                            ErrCode = -1;
                            return;
                    end

                otherwise

                    fprintf (1, '\nОшибка функции Repair_RutFast_Coord: недопустимый тип property set [%s] среди элементов присоединенной топологии [%s] болта %i !\n', pset_type, DB_FASTENERS{Fastener_Index}.Joint_topology{i}, Fastener_Index);
                    ErrCode = -1;
                    return;

            end

        case {'rbe2_3d_hole', 'bar_spider_3d_hole'}

            % Проверка типа материала
            mat_type = DB_MATERIALS{AT_MATERIALS(DB_PSETS{AT_PSETS(DB_ELEMENTS{AT_ELEMENTS(DB_FASTENERS{Fastener_Index}.Joint_mesh{i}.Element_list(1))}.Prop_ID)}.Mat_ID)}.type;

            switch mat_type
                case 'isotropic'
                    CM_Flag = 0;
                otherwise
                    fprintf (1, '\nОшибка функции Repair_RutFast_Coord: недопустимый тип материала [%s] среди элементов присоединенной топологии [%s] болта %i !\n', mat_type, DB_FASTENERS{Fastener_Index}.Joint_topology{i}, Fastener_Index);
                    ErrCode = -1;
                    return;
            end

        otherwise

            fprintf (1, '\nОшибка функции Repair_RutFast_Coord: неизвестный тип присоединенной топологии [%s] в болте %i !\n', DB_FASTENERS{Fastener_Index}.Joint_topology{i}, Fastener_Index);
            ErrCode = -1;
            return;

    end
    
    % Если ярус с композитной пластиной
    if (CM_Flag == 1)
        
        for j=1:size(DB_FASTENERS{Fastener_Index}.Bush_elements{i},1)

            Ori_CID = Get_Bush_Ori_CID(DB_FASTENERS{Fastener_Index}.Bush_elements{i}(j));

            counter = counter + 1;
            
            CM_Coord_list(counter,1) = Ori_CID;
            
        end
        
    end
    
end

if (counter == 0)
    
    CM_Coord_list = [];
    
else
    
    % Финализация массива
    CM_Coord_list = CM_Coord_list(1:counter);
    
    % Оставляем уникальные элементы
    CM_Coord_list = unique(CM_Coord_list);
    
end

% Формирование упорядоченного списка узлов болта

Fastener_Node_list = zeros(3*DB_FASTENERS{Fastener_Index}.N_layers + 2,1);

if (DB_FASTENERS{Fastener_Index}.Head_MPC_Index == 1)
    
    Fastener_Node_list(1 : DB_FASTENERS{Fastener_Index}.N_layers + 2) = DB_FASTENERS{Fastener_Index}.Shank_nodes;
    Fastener_Node_list(  DB_FASTENERS{Fastener_Index}.N_layers + 3 : 2*DB_FASTENERS{Fastener_Index}.N_layers + 2) = DB_FASTENERS{Fastener_Index}.Additional_nodes;
    Fastener_Node_list(2*DB_FASTENERS{Fastener_Index}.N_layers + 3 : 3*DB_FASTENERS{Fastener_Index}.N_layers + 2) = DB_FASTENERS{Fastener_Index}.Joint_nodes;
    
else
    
    Fastener_Node_list(1 : DB_FASTENERS{Fastener_Index}.N_layers + 2) = flip(DB_FASTENERS{Fastener_Index}.Shank_nodes,1);
    Fastener_Node_list(  DB_FASTENERS{Fastener_Index}.N_layers + 3 : 2*DB_FASTENERS{Fastener_Index}.N_layers + 2) = flip(DB_FASTENERS{Fastener_Index}.Additional_nodes,1);
    Fastener_Node_list(2*DB_FASTENERS{Fastener_Index}.N_layers + 3 : 3*DB_FASTENERS{Fastener_Index}.N_layers + 2) = flip(DB_FASTENERS{Fastener_Index}.Joint_nodes,1);
    
end

% Тест на одну и ту же систему координат во всех узлах

First_Coord_ID = DB_NODES{AT_NODES(Fastener_Node_list(1))}.Analysis_CID;

Fail_flag = 0;

for i=1:size(Fastener_Node_list,1)
    
    if (DB_NODES{AT_NODES(Fastener_Node_list(i))}.Analysis_CID ~= First_Coord_ID)
        Fail_flag = 1;
        break;
    end
    
    if (DB_NODES{AT_NODES(Fastener_Node_list(i))}.Reference_CID ~= First_Coord_ID)
        Fail_flag = 1;
        break;
    end
    
end

% Обработка результатов теста
if (Fail_flag == 1)
    
    % Необходима починка
    Repair_flag = 1;
    
else
    
    % Тест системы координат первого узла на пригодность в качестве общей СК болта
    if (Fast_General_Coord_Test(First_Coord_ID, Axis_Point_1, Axis_Point_2, Axis_vector, CM_Coord_list) < 0)
    
        % Необходима починка
        Repair_flag = 1;
        
    else
        
        % Починка не нужна
        Repair_flag = 0;
        
    end

end

% Выход из функции, если починка не требуется
if (Repair_flag == 0)
    return;
end

% --- Починка систем координат ---

Remove_Coord_Array = zeros(100,1);
Remove_Coord_Counter = 0;

% Поиск подходящей СК из уже имеющихся

Find_flag = 0;

for i=1:size(Fastener_Node_list,1)
    
    if (Fast_General_Coord_Test(DB_NODES{AT_NODES(Fastener_Node_list(i))}.Analysis_CID, Axis_Point_1, Axis_Point_2, Axis_vector, CM_Coord_list) == 0)
        Found_Coord_ID = DB_NODES{AT_NODES(Fastener_Node_list(i))}.Analysis_CID;
        Find_flag = 1;
        break;
    end
    
    if (Fast_General_Coord_Test(DB_NODES{AT_NODES(Fastener_Node_list(i))}.Reference_CID, Axis_Point_1, Axis_Point_2, Axis_vector, CM_Coord_list) == 0)
        Found_Coord_ID = DB_NODES{AT_NODES(Fastener_Node_list(i))}.Reference_CID;
        Find_flag = 1;
        break;
    end
    
end

% Обработка результатов поиска
if (Find_flag == 1)
    
    General_Coord_ID = Found_Coord_ID;
    
else % Создаем общую систему координат
    
    % Индекс яруса со стороны головки болта
    if (DB_FASTENERS{Fastener_Index}.Head_MPC_Index == 1)
        Head_Layer = 1;
    else
        Head_Layer = DB_FASTENERS{Fastener_Index}.N_layers;
    end

    % Получение точек для расчета направления
    Point_1 = Get_Plate_Midsurf_Point(Fastener_Index, Head_Layer, Axis_Point_1, Axis_Point_2, Axis_vector);

    if (ErrCode < 0)
        return;
    end

    Point_2 = Get_Plate_Midsurf_Point(Fastener_Index, DB_FASTENERS{Fastener_Index}.N_layers-Head_Layer+1, Axis_Point_1, Axis_Point_2, Axis_vector);

    if (ErrCode < 0)
        return;
    end

    if (Equiv_Check_Cube(Point_1, Point_2))
        fprintf (1, '\nОшибка: нулевое расстояние между крайними срединными точками в болте Рутмана с номером %i !\n', Fastener_Index);
        ErrCode = -1;
        return;
    end
    
    Midsurface_Point = Point_1;

    % Получение знака
    Sign = sign(dot(Point_1 - Point_2, Axis_vector));

    % Толщина текущего яруса
    Plate_Thickness = Get_Plate_Thickness(Fastener_Index, Head_Layer, Axis_Point_1, Axis_Point_2);

    if (ErrCode < 0)
        return;
    end

    % Координаты начала системы координат
    Origin = Midsurface_Point + Plate_Thickness/2 * Axis_vector * Sign;
    
    % Координаты точки на оси 3
    Point_on_axis_3 = Midsurface_Point + Plate_Thickness * Axis_vector * Sign;
    
    % Получение направления оси X общей СК болта
    [Ref_Dir_Origin, Ref_Dir_Vector] = Get_Fast_Coord_X_Direction(Fastener_Index, Head_Layer, Axis_Point_1, Axis_Point_2, Axis_vector);

    if (ErrCode < 0)
        return;
    end
    
    % Координаты точки в плоскости 13
    Point_on_plane_13 = Ref_Dir_Origin + Ref_Dir_Vector;
    
    % Создание системы координат
    Append_To_Output(PCL_Coord_Create_Rect_3P(Origin, Point_on_axis_3, Point_on_plane_13));
    General_Coord_ID = DB_Coord_Create_Rect_3P(Origin, Point_on_axis_3, Point_on_plane_13);
    db_Next_Coord_Number = db_Next_Coord_Number + 1;
	
    if (ErrCode < 0)
        return;
    end
    
end

% --- Исправление референсных СК в узлах болта ---

Repair_Reference_CID_Node_list = zeros(100,1);
Repair_Reference_CID_Node_counter = 0;

for i=1:size(Fastener_Node_list,1)
    
    if (DB_NODES{AT_NODES(Fastener_Node_list(i))}.Reference_CID ~= General_Coord_ID)
        
        % Добавление узла в список для исправления
        Repair_Reference_CID_Node_counter = Repair_Reference_CID_Node_counter + 1;
        Repair_Reference_CID_Node_list(Repair_Reference_CID_Node_counter) = Fastener_Node_list(i);
        
        % Добавление старой СК в список для удаления
        Remove_Coord_Counter = Remove_Coord_Counter + 1;
        Remove_Coord_Array(Remove_Coord_Counter) = DB_NODES{AT_NODES(Fastener_Node_list(i))}.Reference_CID;
        
        % Обновление счетчика
        Ref_Coord_Repair_Counter = Ref_Coord_Repair_Counter + 1;
        
    end
    
end

% Подготовка массива Repair_Reference_CID_Node_list
if (Repair_Reference_CID_Node_counter == 0)
    Repair_Reference_CID_Node_list = [];
else
    % Финализация массива
    Repair_Reference_CID_Node_list = Repair_Reference_CID_Node_list(1:Repair_Reference_CID_Node_counter);
end

% Исправление референсных СК в узлах
if (~isempty(Repair_Reference_CID_Node_list))
    
    Append_To_Output(PCL_Node_Edit_Coord(Repair_Reference_CID_Node_list, General_Coord_ID, []));
    DB_Node_Edit_Coord(Repair_Reference_CID_Node_list, General_Coord_ID, []);

    if (ErrCode < 0)
        return;
    end
    
end

% --- Исправление СК анализа в узлах болта ---

% Формирование списков узлов
Node_list_1 = Fastener_Node_list(1 : DB_FASTENERS{Fastener_Index}.N_layers + 1); % +1 в конце - это не ошибка

Node_list_2 = zeros(DB_FASTENERS{Fastener_Index}.N_layers + 1,1);
Node_list_2(1:DB_FASTENERS{Fastener_Index}.N_layers) = Fastener_Node_list(  DB_FASTENERS{Fastener_Index}.N_layers + 3 : 2*DB_FASTENERS{Fastener_Index}.N_layers + 2);
Node_list_2(DB_FASTENERS{Fastener_Index}.N_layers + 1) = Fastener_Node_list(DB_FASTENERS{Fastener_Index}.N_layers + 2);

Node_list_3 = Fastener_Node_list(2*DB_FASTENERS{Fastener_Index}.N_layers + 3 : 3*DB_FASTENERS{Fastener_Index}.N_layers + 2);

% Исправление узлов из первого списка
Repair_Analysis_CID_Node_list = zeros(100,1);
Repair_Analysis_CID_Node_counter = 0;

for i=1:size(Node_list_1,1)
    
    if (DB_NODES{AT_NODES(Node_list_1(i))}.Analysis_CID ~= General_Coord_ID)
        
        % Добавление узла в список для исправления
        Repair_Analysis_CID_Node_counter = Repair_Analysis_CID_Node_counter + 1;
        Repair_Analysis_CID_Node_list(Repair_Analysis_CID_Node_counter) = Node_list_1(i);
        
        % Добавление старой СК в список для удаления
        Remove_Coord_Counter = Remove_Coord_Counter + 1;
        Remove_Coord_Array(Remove_Coord_Counter) = DB_NODES{AT_NODES(Node_list_1(i))}.Analysis_CID;
        
        % Обновление счетчика
        An_Coord_Repair_Counter = An_Coord_Repair_Counter + 1;
        
    end
    
end

% Подготовка массива Repair_Analysis_CID_Node_list
if (Repair_Analysis_CID_Node_counter == 0)
    Repair_Analysis_CID_Node_list = [];
else
    % Финализация массива
    Repair_Analysis_CID_Node_list = Repair_Analysis_CID_Node_list(1:Repair_Analysis_CID_Node_counter);
end

% Исправление СК анализа в узлах из первого списка
if (~isempty(Repair_Analysis_CID_Node_list))
    
    Append_To_Output(PCL_Node_Edit_Coord(Repair_Analysis_CID_Node_list, [], General_Coord_ID));
    DB_Node_Edit_Coord(Repair_Analysis_CID_Node_list, [], General_Coord_ID);

    if (ErrCode < 0)
        return;
    end
    
end

% Получение упорядоченного списка RBAR
if (DB_FASTENERS{Fastener_Index}.Head_MPC_Index == 1)
    RBAR_list = DB_FASTENERS{Fastener_Index}.RBAR_MPCs;
else
    RBAR_list = flip(DB_FASTENERS{Fastener_Index}.RBAR_MPCs,1);
end

% Исправление узлов из второго списка
Repair_Analysis_CID_Node_list = zeros(100,1);
Repair_Analysis_CID_Node_counter = 0;

for i=1:size(Node_list_2,1)
    
    if (DB_NODES{AT_NODES(Node_list_2(i))}.Analysis_CID ~= General_Coord_ID)
        
        % Проверка совпадения номера узла
        if (DB_MPC{AT_MPC(RBAR_list(i))}.dep_grid ~= Node_list_2(i))
            fprintf (1, '\nОшибка функции Repair_RutFast_Coord: номер узла из списка Node_list_2 не совпал с номером зависимого узла соответствующего RBAR !\n');
            fprintf (1, 'Обратитесь к разработчику для решения этого вопроса.\n');
            ErrCode = -1;
            return;
        end
        
        % Мэппинг направлений старой системы координат на новую
        Mapping_Table = Coord_Directions_Mapping(DB_NODES{AT_NODES(Node_list_2(i))}.Analysis_CID, General_Coord_ID);
        
        if (ErrCode < 0)
            return;
        end
        
        % Исправление спененей свободы RBAR, если направления не совпадают с исходными
        if (~isequal(Mapping_Table, [1;2;3;4;5;6]))
            
            % Мэппинг зависимых степеней свободы RBAR
            New_Dep_DOFs = Map_DOFs(DB_MPC{AT_MPC(RBAR_list(i))}.dep_DOF, Mapping_Table);
            
            % Получение остальных параметров
            MPC_ID = RBAR_list(i);
            Ind_Node = DB_MPC{AT_MPC(RBAR_list(i))}.ind_grid;
            Dep_Node = DB_MPC{AT_MPC(RBAR_list(i))}.dep_grid;
            
            % Исправление RBAR
            Append_To_Output(PCL_MPC_Modify_RBAR(MPC_ID, Ind_Node, Dep_Node, New_Dep_DOFs));
            DB_MPC_Modify_RBAR(MPC_ID, Ind_Node, Dep_Node, New_Dep_DOFs);
			
			if (ErrCode < 0)
				return;
			end
            
        end
        
        % Добавление узла в список для исправления
        Repair_Analysis_CID_Node_counter = Repair_Analysis_CID_Node_counter + 1;
        Repair_Analysis_CID_Node_list(Repair_Analysis_CID_Node_counter) = Node_list_2(i);
        
        % Добавление старой СК в список для удаления
        Remove_Coord_Counter = Remove_Coord_Counter + 1;
        Remove_Coord_Array(Remove_Coord_Counter) = DB_NODES{AT_NODES(Node_list_2(i))}.Analysis_CID;
        
        % Обновление счетчика
        An_Coord_Repair_Counter = An_Coord_Repair_Counter + 1;
        
    end
    
end

% Подготовка массива Repair_Analysis_CID_Node_list
if (Repair_Analysis_CID_Node_counter == 0)
    Repair_Analysis_CID_Node_list = [];
else
    % Финализация массива
    Repair_Analysis_CID_Node_list = Repair_Analysis_CID_Node_list(1:Repair_Analysis_CID_Node_counter);
end

% Исправление СК анализа в узлах из второго списка
if (~isempty(Repair_Analysis_CID_Node_list))
    
    Append_To_Output(PCL_Node_Edit_Coord(Repair_Analysis_CID_Node_list, [], General_Coord_ID));
    DB_Node_Edit_Coord(Repair_Analysis_CID_Node_list, [], General_Coord_ID);

    if (ErrCode < 0)
        return;
    end
    
end

% Исправление узлов из третьего списка
Repair_Analysis_CID_Node_list = zeros(100,1);
Repair_Analysis_CID_Node_counter = 0;

for i=1:size(Node_list_3,1)
    
    if (DB_NODES{AT_NODES(Node_list_3(i))}.Analysis_CID ~= General_Coord_ID)
        
        % Добавление узла в список для исправления
        Repair_Analysis_CID_Node_counter = Repair_Analysis_CID_Node_counter + 1;
        Repair_Analysis_CID_Node_list(Repair_Analysis_CID_Node_counter) = Node_list_3(i);
        
        % Добавление старой СК в список для удаления
        Remove_Coord_Counter = Remove_Coord_Counter + 1;
        Remove_Coord_Array(Remove_Coord_Counter) = DB_NODES{AT_NODES(Node_list_3(i))}.Analysis_CID;
        
        % Обновление счетчика
        An_Coord_Repair_Counter = An_Coord_Repair_Counter + 1;
        
    end
    
end

% Подготовка массива Repair_Analysis_CID_Node_list
if (Repair_Analysis_CID_Node_counter == 0)
    Repair_Analysis_CID_Node_list = [];
else
    % Финализация массива
    Repair_Analysis_CID_Node_list = Repair_Analysis_CID_Node_list(1:Repair_Analysis_CID_Node_counter);
end

% Исправление СК анализа в узлах из третьего списка
if (~isempty(Repair_Analysis_CID_Node_list))
    
    Append_To_Output(PCL_Node_Edit_Coord(Repair_Analysis_CID_Node_list, [], General_Coord_ID));
    DB_Node_Edit_Coord(Repair_Analysis_CID_Node_list, [], General_Coord_ID);

    if (ErrCode < 0)
        return;
    end
    
end

% Подготовка массива Remove_Coord_Array
if (Remove_Coord_Counter == 0)
    
    Remove_Coord_Array = [];
    
else
    
    % Финализация массива
    Remove_Coord_Array = Remove_Coord_Array(1:Remove_Coord_Counter);
    
    % Оставляем уникальные элементы
    Remove_Coord_Array = unique(Remove_Coord_Array);
    
    % Исключаем глобальную систему координат
    Remove_Coord_Array = setdiff(Remove_Coord_Array, 0);
    
    if (isempty(Remove_Coord_Array))
        Remove_Coord_Array = [];
    end
    
end

% Формирование списка не задействованных систем координат
Non_Used_Coord_list = zeros(100,1);
Non_Used_Coord_counter = 0;

if (~isempty(Remove_Coord_Array))
    
    for i=1:size(Remove_Coord_Array,1)
        
        % Если СК не задействована
        if (DB_Coord_Is_Free(Remove_Coord_Array(i)))
            
            % Добавление узла в список для исправления
            Non_Used_Coord_counter = Non_Used_Coord_counter + 1;
            Non_Used_Coord_list(Non_Used_Coord_counter) = Remove_Coord_Array(i);
            
        end

    end
    
end

% Подготовка массива Non_Used_Coord_list
if (Non_Used_Coord_counter == 0)
    Non_Used_Coord_list = [];
else
    % Финализация массива
    Non_Used_Coord_list = Non_Used_Coord_list(1:Non_Used_Coord_counter);
end

% Удаление не задействованных систем координат
if (~isempty(Non_Used_Coord_list))
    
    Append_To_Output(PCL_Coord_Delete(Non_Used_Coord_list));
    DB_Coord_Delete(Non_Used_Coord_list);

    if (ErrCode < 0)
        return;
    end
    
end
